/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Operations.Geometry;

import Operations.*;
import java.awt.image.BufferedImage;

/**
 *
 * @author littlechild
 */
public class OpAffine implements Operation{

    public BufferedImage operate(BufferedImage image, double[] paras) {
        double s=paras[0];
        double t=paras[1];
        double u=paras[2];
        double theta=paras[3]/360*2*Math.PI;
        //now we compute A
        double a11=s*Math.cos(theta);
        double a12=s*t*u*Math.cos(theta)-s*t*Math.sin(theta);
        double a21=s*Math.sin(theta);
        double a22=s*t*u*Math.sin(theta)+s*t*Math.cos(theta);
        //now we can determine the size of the return image
        Dimension cornerPoints[]=new Dimension[]{
            new Dimension(a11*0+a12*0,a21*0+a22*0),
            new Dimension(a11*image.getWidth()+a12*0,a21*image.getWidth()+a22),
            new Dimension(a11*0+a12*image.getHeight(),a21*0+a22*image.getHeight()),
            new Dimension(a11*image.getWidth()+a12*image.getHeight(),a21*image.getWidth()+a22*image.getHeight())
        };
        Rectangle region=generateRegion(cornerPoints);
        BufferedImage returnImage=new BufferedImage((int)(region.getWidth()+0.99),(int)(region.getHeight()+0.99),image.getType());
        //then we would fill the returnImage according to the formular
        //first generate the A^-1
        double b11=a22/(a11*a22-a12*a21);
        double b12=-a12/(a11*a22-a12*a21);
        double b21=-a21/(a11*a22-a12*a21);
        double b22=a11/(a11*a22-a12*a21);
        //start fill in
        double x0,y0;
        //System.out.println("width:"+returnImage.getWidth()+"; Height"+returnImage.getHeight());
        for(int i=0;i!=returnImage.getWidth();++i) {
            for(int j=0;j!=returnImage.getHeight();++j) {
                x0=b11*(i+region.getX())+b12*(j+region.getY());
                y0=b21*(i+region.getX())+b22*(j+region.getY());
                // may be on the boundary
                if(x0>-1 && y0>-1 && x0<=image.getWidth() && y0<=image.getHeight())
                    returnImage.setRGB(i, j, Utilities.doubleLinearInterpolate(image, x0, y0));
            }
        }
        return returnImage;
    }

    private Rectangle generateRegion(Dimension[] cornerPoints) {
        double leftmost=Double.MAX_VALUE;
        double rightmost=Double.MIN_VALUE;
        double upmost=Double.MAX_VALUE;
        double downmost=Double.MIN_VALUE;
        for(int i=0;i!=cornerPoints.length;++i) {
            if(cornerPoints[i].getWidth()<leftmost)
                leftmost=cornerPoints[i].getWidth();
            if(cornerPoints[i].getWidth()>rightmost)
                rightmost=cornerPoints[i].getWidth();
            if(cornerPoints[i].getHeight()<upmost)
                upmost=cornerPoints[i].getHeight();
            if(cornerPoints[i].getHeight()>downmost) 
                downmost=cornerPoints[i].getHeight();
        }
        return new Rectangle(leftmost,upmost,rightmost-leftmost,downmost-upmost);
    }

}

